﻿using System;

namespace TopHero
{
    public class Hero : BaseEntity
    {
        private readonly Game _game;
        private readonly World _world;
        public World world => _world;
        private IViewObject _viewObject;

        private float _maxSpeed = 1;
        private Vector2 _targetPosition = GameDefine.INVAILD_POSITION;
        private Path _path;
        internal Path path => _path;

        private StateMachine<Hero> _stateMachine;
        public StateMachine<Hero> stateMachine => _stateMachine;

        private PropertyManager _propertyManager;

        internal Hero(Game game, World world)
        {
            _game = game;
            _world = world;
            _stateMachine = new StateMachine<Hero>(this);

            _path = _world.GetRoadPath();
            position = path.CurrentWayPoint();

            InitProperty();

            _stateMachine.ChangeState(HeroIdleState.Instance);
        }

        private void InitProperty()
        {
            _propertyManager = new PropertyManager();
            Property maxHp = _propertyManager.AddProperty(PropertyId.MAX_HP, 100);
            Property hpRate = _propertyManager.AddProperty(PropertyId.HP_RATE, 0.1f);
            Property hp = _propertyManager.AddBasePercentProperty(PropertyId.HP, 50, maxHp, hpRate, maxHp);

            Property atk = _propertyManager.AddProperty(PropertyId.ATK, 10);

            Property maxSpeed = _propertyManager.AddProperty(PropertyId.MAX_SPEED, 1);
            Property speedRate = _propertyManager.AddProperty(PropertyId.SPEED_RATE, 1);
            Property speed =
                _propertyManager.AddBasePercentProperty(PropertyId.SPEED, 0, maxSpeed, speedRate, maxSpeed);
        }

        internal override void Start()
        {
            _viewObject = _game.viewService.CreateHeroView();
            _viewObject.Link(this);
        }

        internal override void Update()
        {
            if (!Dead())
            {
                _propertyManager.Update();
                _stateMachine.Update();
                UpdateMovement();
            }
        }

        internal override void Destroy()
        {
            _viewObject.Unlink();
        }

        internal override bool HandleMessage(Telegram telegram)
        {
            if (_stateMachine.HandleMessage(telegram))
            {
                return true;
            }

            return false;
        }

        internal void UpdateMovement()
        {
            if (_targetPosition == GameDefine.INVAILD_POSITION)
            {
                return;
            }

            position += GetMoveOffset(GameDefine.DELTA_TIME);
        }

        public Vector2 GetInterpolationPosition()
        {
            if (_targetPosition == GameDefine.INVAILD_POSITION)
            {
                return position;
            }

            return position + GetMoveOffset(TickCounter.Instance.interpolationTime);
        }

        public float GetPropertyValue(int id)
        {
            return _propertyManager.GetValue(id);
        }

        public void SetPropertyValue(int id, float value)

        {
            Property property = _propertyManager.GetProperty(id);
            property.value = value;
        }

        private Vector2 GetMoveOffset(float dt)
        {
            Vector2 toTarget = _targetPosition - position;
            float dist = toTarget.magnitude;
            if (dist > 0f)
            {
                float speed = dist / dt;
                speed = Math.Min(_maxSpeed, speed);
                return dt * speed * toTarget / dist;
            }

            return Vector2.zero;
        }

        internal void SetTargetPosition(Vector2 targetPosition)
        {
            _targetPosition = targetPosition;
        }
        
        internal bool Dead()
        {
            return _propertyManager.GetValue(PropertyId.HP) <= 0;
        }
    }
}